﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Engine.Sgml {
    public partial class SElement {
        /// <summary>
        /// Gets the first (in document order) child element with the specified SName.
        /// </summary>
        /// <param name="name">A name to match.</param>
        /// <returns>A SElement that matches the specified SName, or null.</returns>
        public SElement Element(SName name) {
            for (int i = 0; i < Nodes.Count; i++) {
                if (Nodes[i].Name == name)
                    return Nodes[i];
                }
            return null;
            }

        /// <summary>
        /// Returns a collection of the child elements of this element.
        /// </summary>
        /// <returns>An IEnumerable of SElement containing the child elements of this container.</returns>
        public IEnumerable<SElement> Elements() {
            for (int i = 0; i < Nodes.Count; i++) {
                if (Nodes[i].DName != InclusionNode.Instance.Name)
                    yield return Nodes[i];
                }
            }

        /// <summary>
        /// Returns a filtered collection of the child elements of this element. Only elements that have 
        /// a matching name are included in the collection.
        /// </summary>
        /// <param name="name">A name to match.</param>
        /// <returns>An IEnumerable&lt;SElement&gt; containing the children of the element that have a matching <paramref name="name"/>.</returns>
        public IEnumerable<SElement> Elements(SName name) {
            for (int i = 0; i < Nodes.Count; i++) {
                if (Nodes[i].Name == name)
                    yield return Nodes[i];
                }
            }

        /// <summary>
        /// Returns a collection of the sibling elements after this node, in document order.
        /// </summary>
        /// <returns>An IEnumerable of SElement of the sibling elements after this node, in document order.</returns>
        public IEnumerable<SElement> ElementsAfterSelf() {
            SElement next = NextNode;
            while (next != null) {
                if (next.DName != InclusionNode.Instance.Name)
                    yield return next;
                next = next.NextNode;
                }
            }

        /// <summary>
        /// Returns a filtered collection of the sibling elements after this node, in document order.
        /// </summary>
        /// <param name="name">A name to match.</param>
        /// <returns>Elements that have a matching SName.</returns>
        public IEnumerable<SElement> ElementsAfterSelf(SName name) {
            SElement next = NextNode;
            while (next != null) {
                if (next.Name == name)
                    yield return next;
                next = next.NextNode;
                }
            }

        /// <summary>
        /// Returns a collection of the sibling elements before this node, in document order.
        /// </summary>
        /// <returns>An IEnumerable of SElement of the sibling elements before this node, in document order.</returns>
        public IEnumerable<SElement> ElementsBeforeSelf() {
            if (Parent == null)
                yield break;
            SElement e = Parent.FirstNode;
            while (e.OrderIndex < OrderIndex) {
                if (e.DName != InclusionNode.Instance.Name)
                    yield return e;
                e = e.NextNode;
                }
            }

        /// <summary>
        /// Returns a filtered collection of the sibling elements before this node, in document order.
        /// </summary>
        /// <param name="name">A name to match.</param>
        /// <returns>An IEnumerable of SElement of the sibling elements before this node, in document order.</returns>
        public IEnumerable<SElement> ElementsBeforeSelf(SName name) {
            if (Parent == null)
                yield break;
            SElement e = Parent.FirstNode;
            while (e.OrderIndex < OrderIndex) {
                if (e.Name == name)
                    yield return e;
                e = e.NextNode;
                }
            }

        /// <summary>
        /// Returns a collection of the ancestor elements of this node.
        /// </summary>
        /// <returns>An IEnumerable of SElement of the ancestor elements of this node.</returns>
        public IEnumerable<SElement> Ancestors() {
            SElement p = Parent;
            while (p != null) {
                yield return p;
                p = p.Parent;
                }
            }

        /// <summary>
        /// Returns a filtered collection of the ancestor elements of this node.
        /// </summary>
        /// <param name="name">The name to match.</param>
        /// <returns>Elements that have a matching SName.</returns>
        public IEnumerable<SElement> Ancestors(SName name) {
            SElement p = Parent;
            while (p != null) {
                if (p.Name == name)
                    yield return p;
                p = p.Parent;
                }
            }

        /// <summary>
        /// Returns a collection of elements that contain this element, and the ancestors of this element.
        /// </summary>
        /// <returns>An IEnumerable of SElement of elements that contain this element, and the ancestors of this element.</returns>
        public IEnumerable<SElement> AncestorsAndSelf() {
            yield return this;
            SElement p = Parent;
            while (p != null) {
                yield return p;
                p = p.Parent;
                }
            }

        /// <summary>
        /// Returns a filtered collection of elements that contain this element, and the ancestors of this element.
        /// </summary>
        /// <param name="name">A name to match.</param>
        /// <returns>Elements that have a matching SName.</returns>
        public IEnumerable<SElement> AncestorsAndSelf(SName name) {
            if (Name == name)
                yield return this;
            SElement p = Parent;
            while (p != null) {
                if (p.Name == name)
                    yield return p;
                }
            }

        /// <summary>
        /// Returns a collection of the descendant elements for this element, in document order.
        /// </summary>
        /// <returns>An IEnumerable of SElement containing the descendant elements.</returns>
        public IEnumerable<SElement> Descendants() {
            for (int i = 0; i < Nodes.Count; i++) {
                if (Nodes[i].DName != InclusionNode.Instance.Name)
                    yield return Nodes[i];
                if (Nodes[i].HasElements)
                    foreach (var item in Nodes[i].Descendants()) {
                        if (item.DName != InclusionNode.Instance.Name)
                            yield return item;
                        }
                }
            }

        /// <summary>
        /// Returns a filtered collection of the descendant elements for this element, in document order.
        /// </summary>
        /// <param name="name">A name to match.</param>
        /// <returns>Elements that have a matching SName.</returns>
        public IEnumerable<SElement> Descendants(SName name) {
            foreach (SElement item in Descendants()) {
                if (item.Name == name)
                    yield return item;
                }
            }

        /// <summary>
        /// Returns a collection of elements that contain this element, and all descendant elements of this element, in document order.
        /// </summary>
        /// <returns>An IEnumerable of SElement of elements that contain this element, and all descendant elements of this element, in document order.</returns>
        public IEnumerable<SElement> DescendantsAndSelf() {
            yield return this;
            foreach (SElement item in Descendants()) {
                yield return item;
                }
            }

        /// <summary>
        /// Returns a filtered collection of elements that contain this element, and all descendant elements of this element, in document order.
        /// </summary>
        /// <param name="name">A name to match.</param>
        /// <returns>Elements that have a matching SName.</returns>
        public IEnumerable<SElement> DescendantsAndSelf(SName name) {
            if (Name == name)
                yield return this;
            foreach (SElement item in Descendants(name)) {
                yield return item;
                }
            }
        }
    }
